package ynet

import (
	"fmt"
	"golang-zinx-demo/yanx/utils"
	"golang-zinx-demo/yanx/yiface"
)

/*
	消息处理模块的实现
*/
type MsgHandle struct {
	// 存放每个MsgID 所对应的处理方法
	Apis map[uint32]yiface.IRouter
	// 负责worker取任务的队列
	TaskQueue []chan yiface.IRequest
	// 业务工作worker池的worker数量
	WorkPoolSize uint32
}

// 初始化/创建MsgHandle方法
func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:         make(map[uint32]yiface.IRouter),
		WorkPoolSize: utils.GlobalObject.WorkPoolSize, // 从全局配置中获取
		TaskQueue:    make([]chan yiface.IRequest, utils.GlobalObject.WorkPoolSize),
	}
}

// 执行对应的消息处理方法
func (mh *MsgHandle) DoMsgHandle(request yiface.IRequest) {
	// 1 从request中找到MsgID
	handle, ok := mh.Apis[request.GetMsgID()]
	if !ok {
		// id未注册
		fmt.Printf("api msgID = %d,is not found!Need to Register\n", request.GetMsgID())
		return
	}

	// 2 根据MsgID 调度对应的router业务即可
	handle.PreHandle(request)
	handle.Handle(request)
	handle.PostHandle(request)
}

// 为消息添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgID uint32, router yiface.IRouter) {
	// 1 判断 当前msg绑定的API处理函数是否存在
	if _, ok := mh.Apis[msgID]; ok {
		// id已经注册了
		panic(fmt.Sprintf("repeat api,msgID = %d\n", msgID))
	}
	// 2 添加msg与API的绑定关系
	mh.Apis[msgID] = router
	fmt.Printf("Add api MsgID = %d sussfule\n", msgID)
}

// 启动一个Worker工作池 (开启工作池的动作只能发生一次，Yanx框架只能开启一个工作池)
func (mh *MsgHandle) StartWokerPool() {
	// 根据WorkerPoolSize 分别开启Worker，每个Worker用一个go来承载
	for i := 0; i < int(mh.WorkPoolSize); i++ {
		// 一个Worker被启动

		// 1 给当前的Worker对应的Channel消息队列 开辟空间 第0个worker就用第0个channel...
		mh.TaskQueue[i] = make(chan yiface.IRequest, utils.GlobalObject.MaxWorkTaskLen)
		// 2 启动当前的Worker，阻塞等待消息从channel传递进来
		go mh.StartOneWoker(i, mh.TaskQueue[i])
	}
}

// 启动一个Worker工作流程
func (mh *MsgHandle) StartOneWoker(workerID int, taskQueue chan yiface.IRequest) {
	fmt.Printf("WorkerID = %d is starting...\n", workerID)
	// 不断的阻塞等待对应消息队列的消息
	for {
		select {
		case request := <-taskQueue:
			// 如果有消息过来，出列的就是一个客户端的request，执行当前Request所绑定的业务
			mh.DoMsgHandle(request)
		}
	}
}

// 将消息交给TaskQueue 由Worker来处理
func (mh *MsgHandle) SendMsgToTaskQueue(request yiface.IRequest) {
	// 1 将消息平均分配给Worker
	// 根据客户端建立的ConnID来进行分配
	workerID := request.GetConnection().GetConnID() % mh.WorkPoolSize
	fmt.Printf("Add ConnID = %d，request MsgID = %d to workerID = %d\n",
		request.GetConnection().GetConnID(), request.GetMsgID(), workerID)

	// 2 将消息发送给对应的worker的TaskQueue即可
	mh.TaskQueue[workerID] <- request
}
